﻿// YamlWriter.cs
//
// Copyright 2008-2009 by Burt Harris
// Licensed under the Microsoft Public License (Ms-PL)
// See: http://www.codeplex.com/yaml/license
//

namespace YaTools.Yaml
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Runtime.Serialization;
    using YaTools.Yaml.Serialization;

    [Obsolete("Replace with Emitter/Mode/Waypoint")]
    public sealed class YamlWriterSettings
    {
        public int BestWidth;
        public int IndentSize { get; set; }
        public bool CompactNesting;
        public YamlTextWriter.TaggingOption Tagging{ get; set; }

        public YamlWriterSettings()
        {
            CompactNesting = false;
            Tagging = YamlTextWriter.TaggingOption.WhenNeeded;
            IndentSize      = 2;
        }
    }

    [Obsolete("Replace with Emitter/Mode/Waypoint")]
    public abstract class YamlWriter
    {
        public abstract void Alias(string anchorName);
        public abstract void Anchor(string anchorName);
        public abstract void BeginDocument();
        public abstract void BeginMapping(string tag);
        public abstract void BeginSequence(string tag);
        public abstract void EndDocument();
        public abstract void EndMapping();
        public abstract void EndSequence();
        public abstract void Scalar(string tag, string value);
        public virtual  void WriteComment() {}
        public virtual  void WriteLine() { }
    }

    [Obsolete("Replace with Emitter/Mode/Waypoint")]
    public sealed class NullYamlWriter : YamlWriter
    {
        public override void Alias(string anchorName)
        {
        }

        public override void Anchor(string anchorName)
        {
        }

        public override void Scalar(string tag, string value)
        {
        }
        
        public override void BeginDocument()
        {
        }

        public override void BeginMapping(string tag)
        {
        }

        public override void BeginSequence(string tag)
        {
        }

        public override void EndDocument()
        {
        }

        public override void EndMapping()
        {
        }

        public override void EndSequence()
        {
        }
    }

    [Obsolete("Replace with Emitter/Mode/Waypoint")]
    public abstract class OldYamlWriter
    {
        public abstract void WriteLine();
        public abstract void WriteComment(string comment, bool indented);
        public abstract void WriteItem(object value, Type impliedType);
        public abstract void WriteKey(object value, Type impliedType);
        public abstract void WriteScalar(object value, string tagstring );
        public abstract bool BeginList(object value, string tagstring, bool flowRequested );
        public abstract void EndList();
        public abstract bool BeginMap(object value, string tagstring, bool flowRequested );
        public abstract void EndMap();

        [CLSCompliant(false)]
        protected StreamingContext _streamingContext;
        [CLSCompliant(false)]
        protected SurrogateSelector _selector;
        [CLSCompliant(false)]
        protected Dictionary<Type, YamlSurrogate> _surrogates;
        [CLSCompliant(false)]
        protected Dictionary<string, string> _tags;
        [CLSCompliant(false)]
        protected Dictionary<string, string> _namespaces;
        [CLSCompliant(false)]
        protected Dictionary<object, string> _anchors;

        protected OldYamlWriter()
        {
            this._streamingContext = new StreamingContext(
                StreamingContextStates.All, this);
            this._selector = new YamlSurrogateSelector();
            this._surrogates = new Dictionary<Type, YamlSurrogate>();
            this._tags = new Dictionary<string, string>();
            // this._namespaces;
            // this._anchors;
        }
        
        protected OldYamlWriter(OldYamlWriter parent)
        {
            this._streamingContext = new StreamingContext(
                StreamingContextStates.All, this);
            this._selector = parent._selector;
            this._surrogates = parent._surrogates;
            this._tags = parent._tags;
            this._namespaces = parent._namespaces;
            this._anchors = parent._anchors;
        }

        static protected readonly char[] s_uriBreakCharacters = new char[] 
            { '.', ',', ':', '/', '\\' };

        protected void AddTag(string uri, string tag)
        {
            Debug.Assert(tag[0] == '!');
            Debug.Assert(tag[tag.Length - 1] == ' ');
            _tags.Add(uri, tag);
        }

        protected void Preprocess(object value, Type impliedType)
        {
            if (_tags == null) _tags = new Dictionary<string, string>();

            YamlPreliminaryWriter preprocessor = new YamlPreliminaryWriter(this, false);
            preprocessor.WriteItem(value, impliedType);

        }


        protected struct ObjectInfo
        {
            public int count;
            // TODO: better heuristics...
            // public int approximateSize;
        }

        [Serializable]
        [Obsolete("Replace with Emitter/Mode/Waypoint")]
        protected class ObjectInfoBuilder : Dictionary<object, ObjectInfo>
        {
            public bool Visit(object value)
            {
                ObjectInfo info;
                if (TryGetValue(value, out info))
                {
                    info.count++;
                    this[value] = info;
                    return true;
                }
                else
                {
                    info.count = 1;
                    this.Add(value, info);
                    return false;
                }
            }
        }

        class YamlPreliminaryWriter : OldYamlWriter
        {
            bool _anchorStrings;
            ObjectInfoBuilder _builder = new ObjectInfoBuilder();


            public YamlPreliminaryWriter(OldYamlWriter parent, bool anchorStrings) 
                : base(parent)
            {
                _anchorStrings = anchorStrings;
            }

            public override void WriteLine()
            {
            }

            public override void WriteComment(string comment, bool indented)
            {
            }

            public override void WriteItem(object value, Type impliedType)
            {
                /// Collect data to determine if this object should be anchored

                if (value == null) return;
                if (value is string && !_anchorStrings) return;
                if (!(value is ValueType))
                {
                    if (_builder.Visit(value))
                        return; // Seen it before, don't bother with type processing
                }

                /// Gather information about the value's type

                Type objectType = value.GetType();


                YamlSurrogate surrogate = null;
                if (!_surrogates.TryGetValue(objectType, out surrogate))
                {
                    if (Type.GetTypeCode(objectType) != TypeCode.Object)
                    {
                        surrogate = ConvertSurrogate.Instance;
                    }
                    else
                    {
                        ISurrogateSelector selectedBy;
                        ISerializationSurrogate ss = _selector.GetSurrogate(
                            objectType, _streamingContext, out selectedBy);

                        if (ss == null)
                        {
                            throw new SerializationException( string.Format( 
                                "Unable to locate serialization surrogate for {0}",
                                objectType ));
                        }
                        
                        surrogate = ss as YamlSurrogate;
                        if (surrogate == null)
                            surrogate = new SurrogateWrapper(ss);
                    }
                    _surrogates[objectType] = surrogate;
                }
                if (surrogate == null)
                    throw new ArgumentException("value is non-serialiable");

                surrogate.GetObjectData(value, null, _streamingContext);
            }

            private void GenerateAnchors()
            {
                // Find out just the objects that have been referenced multiply

                this._anchors = new Dictionary<object, string>();
                foreach (KeyValuePair<object, ObjectInfo> pair in _builder)
                    if (pair.Value.count > 1)
                    {
                        _anchors.Add(pair.Key, null);
                    }

                this._builder = null;	// Done with these stats
            }


            public override void WriteKey(object value, Type impliedType)
            {
                WriteItem(value, impliedType);
            }

            public override void WriteScalar(object value, string tagUri)
            {
                WriteItem(value, null);
            }

            public override bool BeginList(object value, string tagUri, bool flowRequested)
            {
                return true;
            }

            public override void EndList()
            {
            }

            public override bool BeginMap(object value, string tagUri, bool flowRequested)
            {
                return true;
            }

            public override void EndMap()
            {
            }
        }
    }

}
